import axios from 'axios'
import type {
  AxiosError,
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  CreateAxiosDefaults,
  InternalAxiosRequestConfig
} from 'axios'
import type { App } from 'vue'

declare module 'vue' {
  interface ComponentCustomProperties {
    $http: HttpContainerType
  }
}

export interface Options<D = any, R = any> extends CreateAxiosDefaults<D> {
  dataField?: string
  requestInterceptor?: (config: InternalAxiosRequestConfig<D>) => InternalAxiosRequestConfig<D>
  requestInterceptorError?: (error: AxiosError<R, D>) => any
  responseInterceptor?: (response: AxiosResponse<R, D>) => AxiosResponse<R, D>
  responseInterceptorError?: (error: AxiosError<R, D>) => any
}

export interface AxiosRequestOptions<D = any, R = any> extends AxiosRequestConfig<D> {
  // 当你的某个请求想要跳过错误处理时，可以通过将skipErrorHandler设为 true 来实现
  skipErrorHandler: boolean
  // request 默认返回的是你后端的数据，如果你想要拿到 axios 完整的 response 结构，可以通过传入 { getResponse: true } 来实现。
  getResponse: boolean
  requestInterceptor?: (config: InternalAxiosRequestConfig<D>) => InternalAxiosRequestConfig<D>
  requestInterceptorError?: (error: AxiosError<R, D>) => any
  responseInterceptor?: (response: AxiosResponse<R, D>) => AxiosResponse<R, D>
  responseInterceptorError?: (error: AxiosError<R, D>) => any
}

export type RequestMapType = Record<
  string,
  Pick<
    AxiosRequestOptions,
    | 'skipErrorHandler'
    | 'getResponse'
    | 'requestInterceptor'
    | 'requestInterceptorError'
    | 'responseInterceptor'
    | 'responseInterceptorError'
  >
>

export type HttpContainerType = Record<string, AxiosInstance>

const HttpContainer: HttpContainerType = {}

export const defineHttp = <R = any, D = any>(id: string, options: Options) => {
  const {
    dataField,
    requestInterceptor,
    requestInterceptorError,
    responseInterceptor,
    responseInterceptorError,
    ...config
  } = options
  const instance = axios.create(config)
  const requestMap: RequestMapType = {}

  instance.interceptors.request.use(
    (config) => {
      // 全局事件，事件有异常则调用 error
      if (requestInterceptor) {
        return requestInterceptor(config)
      }
      return config
    },
    (error) => {
      if (requestInterceptorError) {
        return requestInterceptorError(error as AxiosError)
      }
      return Promise.reject(error)
    }
  )

  instance.interceptors.response.use(
    (response) => {
      // 全局事件，事件有异常则调用 error
      if (responseInterceptor) {
        return responseInterceptor(response)
      }

      const { config } = response
      let getResponse = false
      if (config.url && config.url in requestMap) {
        getResponse = requestMap[config.url]['getResponse']
      }
      if (getResponse) {
        return response
      }

      if (dataField && dataField in response.data) {
        return response.data[dataField]
      }

      return response.data
    },
    (error) => {
      // error === throw AxiosError(code, config, message, name, request, response, stack)
      if (responseInterceptorError) {
        return responseInterceptorError(error as AxiosError)
      }
      return Promise.reject(error)
    }
  )

  HttpContainer[id] = instance

  return async (url: string, options: Partial<AxiosRequestOptions<D, R>>) => {
    const {
      skipErrorHandler = true,
      getResponse = false,
      requestInterceptor,
      requestInterceptorError,
      responseInterceptor,
      responseInterceptorError,
      ...config
    } = options

    requestMap[url] = {
      skipErrorHandler,
      getResponse,
      requestInterceptor,
      requestInterceptorError,
      responseInterceptor,
      responseInterceptorError
    }
    return instance.request<R>({
      ...config,
      url
    })
  }
}

export const createHttp = () => ({
  install(app: App) {
    app.config.globalProperties.$http = HttpContainer
  }
})
